package com.tentcoo.util;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.UndeclaredThrowableException;

/**
 * 反射工具类。
 * 
 * @author Allenz
 * @since 0.03-SNAPSHOT
 */
public abstract class ReflectionUtils {

	/**
	 * 获得泛型泛型声明的类型。例如声明类：<br/>
	 * <code>public BookManager extends GenricManager&lt;Book&gt;</code><br/>
	 * <br/>
	 * <code>getSuperClassGenericType(BookManager.class)</code>将得到
	 * <code>Book.class</code> 。
	 * 
	 * @param clazz
	 *            目标类型
	 * @return 第一个泛型声明的类型，如果不能确定将返回<code>Object.class</code>
	 */
	@SuppressWarnings("rawtypes")
	public static Class getSuperClassGenericType(Class clazz) {
		return getSuperClassGenericType(clazz, 0);
	}

	/**
	 * 获得泛型泛型声明的类型。例如声明类：<br/>
	 * <code>public BookManager extends GenricManager&lt;Book&gt;</code><br/>
	 * <br/>
	 * <code>getSuperClassGenericType(BookManager.class, 0)</code>将得到
	 * <code>Book.class</code> 。
	 * 
	 * @param clazz
	 *            目标类型
	 * @param index
	 *            泛型声明的序号，从0开始
	 * @return 该序号位置泛型声明的类型，如果不能确定将返回<code>Object.class</code>
	 * @throws IndexOutOfBoundsException
	 *             序号大于或等于泛型声明数目时抛出
	 */
	@SuppressWarnings("rawtypes")
	public static Class getSuperClassGenericType(Class clazz, int index)
			throws IndexOutOfBoundsException {
		AssertUtils.notNull(clazz);
		AssertUtils.isTrue(index >= 0);

		Type genType = clazz.getGenericSuperclass();

		if (!(genType instanceof ParameterizedType)) {
			return Object.class;
		}

		Type[] params = ((ParameterizedType) genType).getActualTypeArguments();

		if (index >= params.length) {
			throw new IndexOutOfBoundsException("泛型索引号超出范围");
		}
		if (!(params[index] instanceof Class)) {
			return Object.class;
		}
		return (Class) params[index];
	}

	/**
	 * Handle the given reflection exception. Should only be called if no
	 * checked exception is expected to be thrown by the target method.
	 * <p>
	 * Throws the underlying RuntimeException or Error in case of an
	 * InvocationTargetException with such a root cause. Throws an
	 * IllegalStateException with an appropriate message else.
	 * 
	 * @param ex
	 *            the reflection exception to handle
	 */
	public static void handleReflectionException(Exception ex) {
		if (ex instanceof NoSuchMethodException) {
			throw new IllegalStateException("Method not found: "
					+ ex.getMessage());
		}
		if (ex instanceof IllegalAccessException) {
			throw new IllegalStateException("Could not access method: "
					+ ex.getMessage());
		}
		if (ex instanceof InvocationTargetException) {
			handleInvocationTargetException((InvocationTargetException) ex);
		}
		if (ex instanceof RuntimeException) {
			throw (RuntimeException) ex;
		}
		throw new UndeclaredThrowableException(ex);
	}

	/**
	 * Handle the given invocation target exception. Should only be called if no
	 * checked exception is expected to be thrown by the target method.
	 * <p>
	 * Throws the underlying RuntimeException or Error in case of such a root
	 * cause. Throws an IllegalStateException else.
	 * 
	 * @param ex
	 *            the invocation target exception to handle
	 */
	public static void handleInvocationTargetException(
			InvocationTargetException ex) {
		rethrowRuntimeException(ex.getTargetException());
	}

	/**
	 * Rethrow the given {@link Throwable exception}, which is presumably the
	 * <em>target exception</em> of an {@link InvocationTargetException}. Should
	 * only be called if no checked exception is expected to be thrown by the
	 * target method.
	 * <p>
	 * Rethrows the underlying exception cast to an {@link RuntimeException} or
	 * {@link Error} if appropriate; otherwise, throws an
	 * {@link IllegalStateException}.
	 * 
	 * @param ex
	 *            the exception to rethrow
	 * @throws RuntimeException
	 *             the rethrown exception
	 */
	public static void rethrowRuntimeException(Throwable ex) {
		if (ex instanceof RuntimeException) {
			throw (RuntimeException) ex;
		}
		if (ex instanceof Error) {
			throw (Error) ex;
		}
		throw new UndeclaredThrowableException(ex);
	}
}
